package com.airsystem.pos.bengkel.controller;

import java.awt.Frame;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import javax.accessibility.Accessible;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.JTextField;

import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.view.JasperViewer;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.airsystem.pos.bengkel.component.TableFrame;
import com.airsystem.pos.bengkel.entity.DetailPesanSparepart;
import com.airsystem.pos.bengkel.entity.DetailSparepart;
import com.airsystem.pos.bengkel.entity.PesanSparepart;
import com.airsystem.pos.bengkel.entity.wrapper.PesanSparepartWrapper;
import com.airsystem.pos.bengkel.interfaces.IParser;
import com.airsystem.pos.bengkel.interfaces.IServices;
import com.airsystem.pos.bengkel.interfaces.ITable;
import com.airsystem.pos.bengkel.interfaces.ITransaction;
import com.airsystem.pos.bengkel.interfaces.IValidator;
import com.airsystem.pos.bengkel.model.SparepartOrderModel;
import com.airsystem.pos.bengkel.service.PesanSparepartServices;
import com.airsystem.pos.bengkel.service.SparepartServices;
import com.airsystem.pos.bengkel.service.converter.SparepartTableConverter;
import com.airsystem.pos.bengkel.util.FormValidator;
import com.airsystem.pos.bengkel.util.HibernateUtil;
import com.airsystem.pos.bengkel.view.dialog.MasterSparepartDialog;
import com.airsystem.pos.bengkel.view.dialog.SparepartOrderDialog;

/**
 * @author Fanny Irawan Sutawanir (fannyirawans@gmail.com)
 */
public class SparepartOrderController extends SparepartOrderModel implements ITransaction, ITable {
	private static final Logger LOG = Logger.getLogger(SparepartOrderController.class.getSimpleName());

	private static final String BRANDS = "%s - %s";

	private final IServices<PesanSparepart> pesanSparepartServices;
	private final IServices<DetailSparepart> detailSparepartServices;

	private final IParser<PesanSparepart, JTable> gridConverter;

	private final IValidator<Accessible> formValidator;

	private PesanSparepartWrapper selectedWrapper;

	private Long ID;

	private Long ID_PESAN;

	public SparepartOrderController() {
		super();

		pesanSparepartServices = new PesanSparepartServices();
		detailSparepartServices = new SparepartServices();

		gridConverter = new SparepartTableConverter();

		formValidator = new FormValidator();
	}

	/**
	 * UI State
	 */
	public void setup() {
		updateButton.setVisible(false);
		deleteButton.setVisible(false);

		submitButton.setEnabled(false);

		tanggalChooser.setDate(new Date());
	}

	public void reset() {
		sparepartField.setText("");
		kendaraanField.setText("");
		jenisField.setText("");
		hargaField.setText("");
		orderField.setText("");
		distributorField.setText("");
		pembayaranCombo.setSelectedIndex(0);
	}

	/**
	 * Database Action
	 */
	public void search() {
		dialogSparepartOrder = new SparepartOrderDialog();

		try {
			if (dialogSparepartOrder.getDataPesanSparepart() != null) {
				reset();
				TableFrame.deleteTable(tableSpareparts, tableModel);

				selectedWrapper = dialogSparepartOrder.getDataPesanSparepart();

				tanggalChooser.setDate(selectedWrapper.getPesanSparepart().getTanggal());

				for (DetailPesanSparepart item : selectedWrapper.getPesanSparepart().getDetail_pesan_sparepart()) {
					String sparepart = createBrands(item.getDetail_sparepart().getSparepart().getMerk(),
													item.getDetail_sparepart().getSub_merk());

					String kendaraan = createBrands(item.getDetail_sparepart().getDetail_kendaraan().getKendaraan().getMerk(),
													item.getDetail_sparepart().getDetail_kendaraan().getSub_merk());

					String jenisSparepart = item.getDetail_sparepart().getJenis_sparepart().getNama();
					String distributor = item.getDistributor();
					String metode = item.getMetode_s();
					String ID = item.getDetail_sparepart().getJenis_sparepart().getId_s();

					Long pesan = item.getStok();

					BigDecimal harga = new BigDecimal(item.getHarga().toBigInteger());
					BigDecimal total = new BigDecimal(pesan).multiply(harga).setScale(2, BigDecimal.ROUND_HALF_EVEN);

					Vector<String> vector = new Vector<String>();
					vector.add(sparepart);
					vector.add(kendaraan);
					vector.add(jenisSparepart);
					vector.add(harga.toString().replace(".00", ""));
					vector.add(pesan.toString());
					vector.add(total.toString().replace(".00", ""));
					vector.add(distributor);
					vector.add(metode);
					vector.add(ID);

					tableModel.addRow(vector);
				}
				tableSpareparts.setModel(tableModel);

				doCount();
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		}
	}

	public void get() {
		dialogMasterSparepart = new MasterSparepartDialog();

		try {
			if (dialogMasterSparepart.getDetailSparepart() != null) {
				DetailSparepart detailSparepart = dialogMasterSparepart.getDetailSparepart();

				sparepartField.setText(createBrands(detailSparepart.getSparepart().getMerk(),
													detailSparepart.getSub_merk()));

				kendaraanField.setText(createBrands(detailSparepart.getDetail_kendaraan().getKendaraan().getMerk(),
													detailSparepart.getDetail_kendaraan().getSub_merk()));

				jenisField.setText(detailSparepart.getJenis_sparepart().getNama());

				ID = detailSparepart.getJenis_sparepart().getId();
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		}
	}

	public void submit() {
		JTextField dateEditor = ((JTextField) tanggalChooser.getDateEditor().getUiComponent());

		if (!isFormValid(dateEditor)) {
			return;
		}

		if (tableSpareparts.getRowCount() == 0) {
			JOptionPane.showMessageDialog(this, "Data kosong!", "Pesan", JOptionPane.WARNING_MESSAGE);

			return;
		}

		if (selectedWrapper == null) {
			try {
				PesanSparepart pesanSparepart = gridConverter.doParse(tableSpareparts);
				pesanSparepartServices.saveOrUpdate(pesanSparepart);

				ID_PESAN = pesanSparepart.getId();

				for (DetailPesanSparepart item : pesanSparepart.getDetail_pesan_sparepart()) {
					item.getDetail_sparepart().setChoice(99);

					Iterable<DetailSparepart> spareparts = detailSparepartServices.select(item.getDetail_sparepart());
					for (DetailSparepart sparepart : spareparts) {
						sparepart.setStok(item.getStok() + sparepart.getStok());
						detailSparepartServices.saveOrUpdate(sparepart);

						break;
					}
				}
				JOptionPane.showMessageDialog(this, "Transaksi sukses", "Pesan", JOptionPane.INFORMATION_MESSAGE);

				print();
				doCancel();
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
				JOptionPane.showMessageDialog(this, "Transaksi gagal!", "Pesan", JOptionPane.ERROR_MESSAGE);
			}
		} else {
			Session session = HibernateUtil.getSessionFactory().getCurrentSession();
			Transaction transaction = session.beginTransaction();

			try {
				PesanSparepart pesanSparepart = gridConverter.doParse(tableSpareparts);

				/* Delete all child table */
				for (DetailPesanSparepart detail : selectedWrapper.getPesanSparepart().getDetail_pesan_sparepart()) {
					Long stockOrder = detail.getStok();
					Long currentStock = new Long((long) 0);

					DetailSparepart detailSparepartUpdate = null;
					detail.getDetail_sparepart().setChoice(99);

					Iterable<DetailSparepart> spareparts = detailSparepartServices.select(detail.getDetail_sparepart());
					for (DetailSparepart sparepart : spareparts) {
						currentStock = sparepart.getStok();
						detailSparepartUpdate = sparepart;

						break;
					}

					detailSparepartUpdate.setStok(currentStock - stockOrder);
					detailSparepartServices.saveOrUpdate(detailSparepartUpdate);

					session.delete(detail);
				}
				transaction.commit();

				session = HibernateUtil.getSessionFactory().getCurrentSession();
				transaction = session.beginTransaction();

				pesanSparepart.setId(selectedWrapper.getPesanSparepart().getId());
				ID_PESAN = selectedWrapper.getPesanSparepart().getId();

				/* STOCK = CURRENT - PREV_STOCK_ORDER + CURRENT_STOCK_ORDER */
				for (DetailPesanSparepart detailPesanSparepart : pesanSparepart.getDetail_pesan_sparepart()) {
					Long currentStockOrder = detailPesanSparepart.getStok();
					Long currentStock = new Long((long) 0.0);
					Long resultStock = new Long((long) 0.0);

					DetailSparepart detailSparepartUpdate = null;
					detailPesanSparepart.getDetail_sparepart().setChoice(99);

					Iterable<DetailSparepart> spareparts = detailSparepartServices.select(detailPesanSparepart.getDetail_sparepart());
					for (DetailSparepart sparepart : spareparts) {
						currentStock = sparepart.getStok();
						detailSparepartUpdate = sparepart;

						break;
					}
					resultStock = currentStock + currentStockOrder;

					detailSparepartUpdate.setStok(resultStock);
					detailSparepartServices.saveOrUpdate(detailSparepartUpdate);
				}
				session.saveOrUpdate(pesanSparepart);
				transaction.commit();

				JOptionPane.showMessageDialog(this, "Transaksi sukses", "Pesan", JOptionPane.INFORMATION_MESSAGE);

				print();
				doCancel();
			} catch (RuntimeException e) {
				LOG.error("Submit error is " + e.getMessage(), e);
				JOptionPane.showMessageDialog(this, "Transaksi gagal!", "Pesan", JOptionPane.ERROR_MESSAGE);

				try {
					transaction.rollback();
				} catch (RuntimeException runtimeException) {
					LOG.error("Transaction rollback is " + runtimeException.getMessage(), runtimeException);
				}
			} finally {
				if (session != null && session.isOpen()) {
					session.close();
				}
			}
		}
	}

	public void print() {
		try {
			Object[] objects = { "Ya", "Tidak" };
			int ask = JOptionPane.showOptionDialog(SparepartOrderController.this, "Print transaksi?", "Konfirmasi", JOptionPane.YES_NO_OPTION,
					  JOptionPane.QUESTION_MESSAGE, null, objects, objects[0]);

			if (ask == 0) {
				printTransaction();
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		}
	}

	public void cancel() {
		if (isGridClicked) {
			tableSpareparts.clearSelection();

			row = -1;
			reset();
			isGridClicked = false;
		} else {
			doCancel();
		}
	}

	/**
	 * Grid Action
	 */
	public void init() {
		String sparepart = tableSpareparts.getValueAt(row, 0).toString();
		String kendaraan = tableSpareparts.getValueAt(row, 1).toString();
		String jenisSparepart = tableSpareparts.getValueAt(row, 2).toString();
		String harga = tableSpareparts.getValueAt(row, 3).toString();
		String order = tableSpareparts.getValueAt(row, 4).toString();
		String distributor = tableSpareparts.getValueAt(row, 6).toString();
		String metode = tableSpareparts.getValueAt(row, 7).toString();
		String jenissparepartID = tableSpareparts.getValueAt(row, 8).toString();

		ID = Long.parseLong(jenissparepartID);

		sparepartField.setText(sparepart);
		kendaraanField.setText(kendaraan);
		jenisField.setText(jenisSparepart);
		hargaField.setText(harga);
		orderField.setText(order);
		distributorField.setText(distributor);
		pembayaranCombo.setSelectedItem(metode);

		updateButton.setVisible(true);
		deleteButton.setVisible(true);

		doCount();
	}

	public void add() {
		if (!isFormValid(sparepartField,
						 kendaraanField,
						 jenisField,
						 orderField,
						 hargaField,
						 distributorField)) {
			return;
		}

		boolean isExist = false;
		for (int i = 0; i < tableSpareparts.getRowCount(); i++) {
			if (!sparepartField.getText().isEmpty()
			&&  sparepartField.getText().equalsIgnoreCase(tableSpareparts.getValueAt(i, 0).toString())) {
				isExist = true;
			}
		}

		if (isExist) {
			JOptionPane.showMessageDialog(this, "Data sudah ada!", "Pesan", JOptionPane.WARNING_MESSAGE);

			return;
		}

		String sparepart = sparepartField.getText();
		String kendaraan = kendaraanField.getText();
		String jenisSparepart = jenisField.getText();
		String distributor = distributorField.getText();
		String metode = pembayaranCombo.getSelectedItem().toString();

		Long pesan = Long.valueOf(orderField.getText());

		BigDecimal harga = new BigDecimal(hargaField.getText());
		BigDecimal total = new BigDecimal(pesan).multiply(harga).setScale(2, BigDecimal.ROUND_HALF_EVEN);

		Vector<String> vector = new Vector<String>();
		vector.add(sparepart);
		vector.add(kendaraan);
		vector.add(jenisSparepart);
		vector.add(harga.toString().replace(".00", ""));
		vector.add(pesan.toString());
		vector.add(total.toString().replace(".00", ""));
		vector.add(distributor);
		vector.add(metode);
		vector.add(String.valueOf(ID));

		tableModel.addRow(vector);
		tableSpareparts.setModel(tableModel);

		submitButton.setEnabled(true);

		doCount();
		reset();
	}

	public void edit() {
		if (!isFormValid(sparepartField,
						 kendaraanField,
						 jenisField,
						 orderField,
						 hargaField,
						 distributorField)) {
			return;
		}

		String sparepart = sparepartField.getText();
		String kendaraan = kendaraanField.getText();
		String jenisSparepart = jenisField.getText();
		String distributor = distributorField.getText();
		String metode = pembayaranCombo.getSelectedItem().toString();

		Long pesan = Long.valueOf(orderField.getText());

		BigDecimal harga = new BigDecimal(hargaField.getText());
		BigDecimal total = new BigDecimal(pesan).multiply(harga).setScale(2, BigDecimal.ROUND_HALF_EVEN);

		tableSpareparts.setValueAt(sparepart, row, 0);
		tableSpareparts.setValueAt(kendaraan, row, 1);
		tableSpareparts.setValueAt(jenisSparepart, row, 2);
		tableSpareparts.setValueAt(harga.toString().replace(".00", ""), row, 3);
		tableSpareparts.setValueAt(pesan.toString(), row, 4);
		tableSpareparts.setValueAt(total.toString().replace(".00", ""), row, 5);
		tableSpareparts.setValueAt(distributor, row, 6);
		tableSpareparts.setValueAt(metode, row, 7);
		tableSpareparts.setValueAt(ID, row, 8);

		ID = null;

		submitButton.setEnabled(true);

		doCount();
		reset();
	}

	public void remove() {
		tableModel.removeRow(row);
		doCount();
		reset();

		submitButton.setEnabled(true);

		if (tableModel.getRowCount() == 0) {
			doCancel();
		}
	}

	protected void delete() {
		try {
			if (selectedWrapper != null) {
				pesanSparepartServices.delete(selectedWrapper.getPesanSparepart());

				for (DetailPesanSparepart detailPesanSparepart : selectedWrapper.getPesanSparepart().getDetail_pesan_sparepart()) {
					Long stockOrder = detailPesanSparepart.getStok();
					Long currentStock = new Long((long) 0);
					DetailSparepart detailSparepartUpdate = null;

					detailPesanSparepart.getDetail_sparepart().setChoice(99);

					Iterable<DetailSparepart> spareparts = detailSparepartServices.select(detailPesanSparepart.getDetail_sparepart());
					for (DetailSparepart sparepart : spareparts) {
						currentStock = sparepart.getStok();
						detailSparepartUpdate = sparepart;

						break;
					}

					detailSparepartUpdate.setStok(currentStock - stockOrder);
					detailSparepartServices.saveOrUpdate(detailSparepartUpdate);
				}
				JOptionPane.showMessageDialog(this, "Hapus transaksi sukses", "Pesan", JOptionPane.INFORMATION_MESSAGE);

				print();
				doCancel();
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			JOptionPane.showMessageDialog(this, "Hapus transaksi gagal!", "Pesan", JOptionPane.ERROR_MESSAGE);
		}
	}

	private boolean isFormValid(Accessible... component) {
		try {
			return formValidator.isValid(component);
		} catch (IllegalArgumentException e) {
			JOptionPane.showMessageDialog(this, "Data kosong!", "Pesan", JOptionPane.WARNING_MESSAGE);
		} catch (IllegalStateException e) {
			JOptionPane.showMessageDialog(this, "Data kosong!", "Pesan", JOptionPane.WARNING_MESSAGE);
		}

		return false;
	}

	private String createBrands(String... params) {
		return String.format(BRANDS, params[0], params[1]);
	}

	private void doCount() {
		int index = tableSpareparts.getRowCount();
		BigDecimal grandTotal = new BigDecimal(0);

		for (int i = 0; i < index; i++) {
			Long total = Long.valueOf(tableSpareparts.getValueAt(i, 5).toString());
			grandTotal = grandTotal.add(BigDecimal.valueOf(total));
		}

		totalField.setText(String.valueOf(grandTotal.toBigInteger()));
	}

	private void doCancel() {
		TableFrame.deleteTable(tableSpareparts, tableModel);
		totalField.setText("");
		reset();
		setup();
		selectedWrapper = null;
	}

	private void printTransaction() {
		try {
			Map<String, String> map = new ConcurrentHashMap<String, String>();

			for (int i = 0; i < tableSpareparts.getRowCount(); i++) {
				String distributor = tableSpareparts.getValueAt(i, 6).toString().trim();
				String value;

				if ((value = map.get(distributor)) != null) {
					if (value.equalsIgnoreCase(distributor)) {
						continue;
					}
				}
				map.put(distributor, distributor);

				List<DetailPesanSparepart> detailPesanSpareparts = ((PesanSparepartServices) pesanSparepartServices).transactionPrint(ID_PESAN, tanggalChooser.getDate(), distributor);

				JRBeanCollectionDataSource beanCollectionDataSource = new JRBeanCollectionDataSource(detailPesanSpareparts);

				Map<String, Object> parameter = new HashMap<String, Object>();
				parameter.put("image_path", SparepartOrderController.class.getResourceAsStream("/images/report.png"));
				parameter.put("tanggal", tanggalChooser.getDate());
				parameter.put("distributor", distributor);

				InputStream inputStream = SparepartOrderController.class.getResourceAsStream("/reports/PrintSparepartOrder.jasper");

				JasperReport jasperReport = (JasperReport) JRLoader.loadObject(inputStream);

				JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameter, beanCollectionDataSource);

				JasperViewer jasperViewer = new JasperViewer(jasperPrint, false);
				jasperViewer.setTitle("Transaksi Pesanan Sparepart");
				jasperViewer.setExtendedState(Frame.MAXIMIZED_BOTH);
				jasperViewer.setVisible(true);
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			JOptionPane.showMessageDialog(this, "Print transaksi gagal!", "Pesan", JOptionPane.ERROR_MESSAGE);
		}
	}
}